package jxta.test;

import java.io.IOException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;

import jxta.chat.AbstractMessageHandler;
import jxta.chat.BidiPipeListener;
import jxta.chat.ChatGUI;
import jxta.chat.LoginMessageHandler;
import jxta.chat.MessageHandlerFactory;
import jxta.chat.PipeHolder;
import jxta.chat.entities.AbstractMessage;
import jxta.chat.entities.LoginMessage;
import jxta.chat.entities.MessageFactory;
import jxta.chat.entities.MessageParams;
import jxta.chat.entities.VoiceChatMessage;
import jxta.chat.events.JxtaChatEvent;
import jxta.chat.events.JxtaChatListener;
import jxta.chat.module.PipeModuleManager;
import jxta.chat.search.PeerSearcher;
import jxta.chat.util.Constants;
import jxta.chat.util.RemoteMessageSender;
import jxta.chat.util.Constants.ChatInvitaionResponseVals;
import jxta.chat.util.Constants.ChatRequestVals;
import jxta.chat.util.Constants.MessageParamsKeys;
import jxta.chat.util.Constants.MessageTypes;
import net.jxta.credential.AuthenticationCredential;
import net.jxta.discovery.DiscoveryEvent;
import net.jxta.discovery.DiscoveryListener;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.AdvertisementFactory;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.endpoint.MessageTransport;
import net.jxta.exception.PeerGroupException;
import net.jxta.exception.ProtocolNotSupportedException;
import net.jxta.id.IDFactory;
import net.jxta.impl.membership.pse.StringAuthenticator;
import net.jxta.membership.MembershipService;
import net.jxta.peergroup.NetPeerGroupFactory;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.pipe.InputPipe;
import net.jxta.pipe.OutputPipe;
import net.jxta.pipe.OutputPipeEvent;
import net.jxta.pipe.OutputPipeListener;
import net.jxta.pipe.PipeID;
import net.jxta.pipe.PipeMsgEvent;
import net.jxta.pipe.PipeMsgListener;
import net.jxta.pipe.PipeService;
import net.jxta.platform.NetworkConfigurator;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.DiscoveryResponseMsg;
import net.jxta.protocol.ModuleImplAdvertisement;
import net.jxta.protocol.PeerGroupAdvertisement;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.rendezvous.RendezVousService;
import net.jxta.rendezvous.RendezvousEvent;
import net.jxta.rendezvous.RendezvousListener;



public class JxtaManager implements RendezvousListener, DiscoveryListener{
	
	// TODO Remove the members from here
	
	public final static String MESSAGE_NAME_SPACE = "msg";
	public final static String MESSAGE_PEER_DATA = "sendPeerData";
	public final static String BIDI_PIPE_ATTRIBUTE = "bidiPipeID";
	public final static String PEER_GROUP_NAME = "ChatTest";
	public final static String NAME_ATTRIBUTE = "Name";
	public final static int SEARCH_TRESHOLD = 10;
	
	public final static String CHAT_PREFIX = "Chat.";
	
	private List<JxtaChatListener> foundedPeerListeners = new ArrayList<JxtaChatListener>();
	
	// FIXME Move to panel
	
	/*private String keyStorePassword = "oren1234";
	private String identityPassword = "oren1234";*/
	
	public static final byte[] GROUP_CHAT_PIPE_ID = {
        (byte) 0xD1, (byte) 0xD1, (byte) 0xD1, (byte) 0xD1,
        (byte) 0xD1, (byte) 0xD1, (byte) 0xD1, (byte) 0xD1,
        (byte) 0xD1, (byte) 0xD1, (byte) 0xD1, (byte) 0xD1,
        (byte) 0xD1, (byte) 0xD1, (byte) 0xD1, (byte) 0xD1
	};
	
	/**
     * A pre-baked PipeID string
     */
    //public final static String PIPEIDSTR = "urn:jxta:uuid-59616261646162614E50472050325033C0C1DE89719B456691A596B983BA0E1004";
    
    private final static String GROUPIDSTR = "urn:jxta:uuid-1989101414002000081518002844102602";
    
    private final static String DESCSTR = "JxtaTest";
	//public final static String PIPEIDSTR = "urn:jxta:uuid-59616261646162614E50472050325033C0C1DE89719B456691A596B983BA0E1004";
	
    private PeerSearcher peerSearcher = null;
	
	//String peerName;
	private RendezVousService rendezvousNPG;
	
	private static String peerName = "Chat.KobyKarin";	
	
	private NetworkManager networkManager = null;
	
	private DiscoveryService discovery = null;
	private PipeService pipeService = null;
	
	// The network global PeerGroup
	private PeerGroup netPeerGroup = null;
	
	// The chat peer group
	private PeerGroup chatPeerGroup = null;
	
	private OutputPipe outputPipe = null;
	private InputPipe inputPipe = null;
	
	// Flags
	private boolean peerGroupFound = false;
	private boolean outputCreated = false;
	
	private Constants cons;
	
	private List<JxtaChatListener> outputListeners = new ArrayList<JxtaChatListener>();
	private List<JxtaChatListener> peerListeners = new ArrayList<JxtaChatListener>();
	
	private PipeModuleManager pipeManager;
	
	private boolean waitingForInvetation;
	
	private boolean chatInProgress = false;
	private String chatingPeerName = null; 
	
	private ChatGUI screen = null;
	
	public JxtaManager(String instanceName, ChatGUI screen){
		
		if (instanceName.startsWith(CHAT_PREFIX)){
			peerName = instanceName;
		}
		else{
			peerName = CHAT_PREFIX + instanceName;
		}
		
		this.screen = screen;
		
		// Initialize the constants class
		cons = Constants.getInstance(peerName);
		
		initialize();
	}
    
   /* private void initialize(){
    	AbstractConfigurator.
    	Profile p = new Profile(getClass().getResourceAsStream(".jxta/profile.xml"));
        URL home = new URI(new File("/.jxta"));
        Configurator c = new Configurator(p, home);
        
        c.setSecurity("user", "pwd");
        
        try {
          c.save();
        } catch (ConfiguratorException ce) {
          ce.printStackTrace();
        }
    }*/
	
	private void initialize(){
		
		//URI home = new File(System.getProperty("user.home") + "/.jxtaTest").toURI();
		//URI home = new File("./" + peerName + "/.jxta").toURI();
		//URI home = new File("./"+ peerName + "/.jxta").toURI();
		
		
		// TODO Add file creation in temp dir
/*		NetworkConfigurator configurator = new NetworkConfigurator(NetworkConfigurator.EDGE_NODE, home);

        if (configurator.exists())
            return; *///allready configured;

        //boolean viewSuccessFull = m_view.configure(new MyPlatformConfigAccess());
        //if (viewSuccessFull) {
        	// TODO Add instance name from system
		
		
        
		NetworkConfigurator configurator = new NetworkConfigurator(NetworkConfigurator.EDGE_NODE, cons.getJxtaHome());		
		            
			try {
				
				networkManager = new NetworkManager(NetworkManager.ConfigMode.EDGE, peerName, cons.getJxtaHome());
				
			
	            networkManager.setUseDefaultSeeds(true);
	            networkManager.setConfigPersistent(true);
	
	            configurator = networkManager.getConfigurator();
	            //configurator.setPrincipal("KobyKarin");
	            configurator.setPrincipal(peerName);
	            configurator.setDescription(DESCSTR);
	            //configurator.setPassword(keyStorePassword);
	            	            
	            configurator.save();	            	            
	            
	            //startJXTA();
            
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        
	}
	
	/*	private void configureHTTPProxy() {
	        URL hp = null;
	        
	            try {
					hp = new URL("/myjxta/proxy/@http");
				} catch (MalformedURLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

	        System.setProperty("http.proxyHost", hp != null ? hp.getHost() : "");
	        System.setProperty("http.proxyPort", hp != null ? String.valueOf(hp
	                .getPort()) : "");
	    }*/
		
	public void startJXTA(){
		try {
			//networkManager.startNetwork();
            //netPeerGroup = networkManager.getNetPeerGroup();//new NetPeerGroupFactory().getInterface();
			netPeerGroup = new NetPeerGroupFactory().getInterface();
            //npgWrapper = new Group(this, netPeerGroup, null, false);
        } catch (PeerGroupException pge) {
            
        	pge.printStackTrace();
        } 

        /*if (npgWrapper == null) {
            setStatus(STRINGS.getString("error.jxta.start"));
            return;

        }*/
        
        //this.peerName = netPeerGroup.getPeerName();
        // this.preferencesManager = new PreferenceManager(this);
        //this.myPreferenceManager = MyPreferenceManager.getInstance();

        //setStatus(STRINGS.getString("status.jxta.joiningApplicationGroup"));

        //String gid = Constants.getInstance().get(Constants.GROUP_ID, "").trim();

        //final boolean mainGroupIsNetPeerGroup = gid.length() == 0;

        // the normal usecase is that we are running in a dedicated myjxta
        // group... in this usecase the netpeergroup is not important for the user 
        // and therefore should not be visible.
        // however, if the NPG is our application group we want it to be visible
        //npgWrapper.setVisible(mainGroupIsNetPeerGroup);

        //joinGroup(npgWrapper, false, false);

        //netPeerGroup.getMembershipService().

        try {
			PeerGroupAdvertisement pga = create(netPeerGroup, peerName, DESCSTR);
			chatPeerGroup = netPeerGroup.newGroup(pga);
			joinGroup(chatPeerGroup);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        
        rendezvousNPG = netPeerGroup.getRendezVousService();        
        rendezvousNPG.addListener(this);                        
        boolean connected = waitForRendezvousConnection(10000);
        System.out.println(connected);
        
        discovery = netPeerGroup.getDiscoveryService();
        
        //connectToPeerGroup();               
        
        // Koby-Changed the following lines
        
        /*discovery = netPeerGroup.getDiscoveryService();
        pipeService = netPeerGroup.getPipeService();*/
        
        discovery = chatPeerGroup.getDiscoveryService();
        pipeService = chatPeerGroup.getPipeService();
        
        createInputPipe();             
        
        // Koby-Change the following line
        //peerSearcher = new PeerSearcher(netPeerGroup, peerName);
        
        //peerSearcher = new PeerSearcher(chatPeerGroup, peerName);
        
        // Start searching for peers
        //peerSearcher.start();
        //peerSearcher.search();
        
        /*if (chatPeerGroup != null){
        	createInputPipe();
            //createOutputPipe();
        }        
        else{
        	System.out.println("Error joining the chat peer group. there may be problems chating");
        }*/
        
        //discovery.addDiscoveryListener(this);
        
        //networkManager.stopNetwork();
        //initializeGroup(npgWrapper);
        //seedGroup(this.view.getGroup());
     //   myPreferenceManager.init();
        
        // TODO Check if i need the type parameter
        
        pipeManager = new PipeModuleManager(peerName + ".VOIP", null);
        pipeManager.addChatListener(/*new JxtaChatListener(){

			//@Override
			public void jxtaChatEvent(JxtaChatEvent event) {
				
				// Add connected peer to the pipe holder
				if (event.getSource() instanceof LoginMessage){
					Message msg = ((LoginMessage)event.getSource()).getMessage();
					
					AbstractMessageHandler handler = MessageHandlerFactory.getInstance().getMessageHandler(msg, null);
					handler.handleMessage(msg, chatPeerGroup);
				}
				else if (event.getSource() instanceof VoiceChatMessage){
					if (isChatInProgress()){
						
						MessageParams pm = new MessageParams(peerName);
						pm.addParameter(MessageParamsKeys.RESPONSE, ChatInvitaionResponseVals.BUSY);						
						AbstractMessage am = MessageFactory.getInstance().getMessageClass(MessageTypes.VOIP_CHAT);						
						Message m = am.createMessage(pm);
						
						(new RemoteMessageSender()).sendMessage(JxtaManager.this, m, ((AbstractMessage)event.getSource()).getPeerName());
						
						// We responded to the invitation so we'll exit
						return;
					}					
				}
				
				notifyPeerEvent(event);
			}
        }*/ new ChatBiDiPipeListener());
        PipeAdvertisement bidiPipe = pipeManager.initialize(chatPeerGroup);
		
        // FIXME disable the sleep
        
        try{
        	Thread.currentThread().sleep(5000);
        }
        catch (Exception e){
        	e.printStackTrace();
        }
        
        this.sendLoginMessage(bidiPipe);
	}
	
	private boolean waitForRendezvousConnection(long timeout) {

        if (0 == timeout) {
            timeout = Long.MAX_VALUE;
        }

        long timeoutAt = System.currentTimeMillis() + timeout;

        if (timeoutAt <= 0) {
            // handle overflow.
            timeoutAt = Long.MAX_VALUE;
        }

        while (!rendezvousNPG.isConnectedToRendezVous() && !rendezvousNPG.isRendezVous()) {
            try {
                long waitFor = timeoutAt - System.currentTimeMillis();

                if (waitFor > 0) {                	
                    Thread.currentThread().sleep(timeout);
                } else {
                    // all done with waiting.
                    break;
                }
            } catch (InterruptedException e) {
                Thread.interrupted();
                break;
            }
        }

        return rendezvousNPG.isConnectedToRendezVous() || rendezvousNPG.isRendezVous();
    }
	
	public void setName(String pName){
		this.peerName = pName;
	}
	
	public void closeNetwork(){
		sendLogOutMessage();
		PipeHolder.getInstance().removeAll();		
		networkManager.stopNetwork();
	}
	
	/**
	 * Notify all the other peers that the current peer is logging out
	 */
	private void sendLogOutMessage(){
		AbstractMessage msg = MessageFactory.getInstance().getMessageClass(MessageTypes.LOGOUT);
		MessageParams params = new MessageParams(peerName);
		sendMessage(msg.createMessage(params));
	}
	
	public void sendChatInvitation(String peerName){
		RemoteMessageSender rms = new RemoteMessageSender();
		rms.setListener(new ChatInvitation());
		
		AbstractMessage am = MessageFactory.getInstance().getMessageClass(MessageTypes.VOIP_CHAT);
		MessageParams mp = new MessageParams(this.peerName);
		mp.addParameter(MessageParamsKeys.REQUEST, ChatRequestVals.INVITATION_REQUEST);		
		
		rms.sendMessage(this, am.createMessage(mp), 20000, peerName);
	}
	
	// TODO Check if we need the parameter useAutoRdvMode
	
	public void joinGroup(PeerGroup peerGroup){
		
		
		MessageTransport tls =
			peerGroup.getEndpointService().getMessageTransport("jxtatls");
		PeerGroup pg = null;

		if (tls != null) {
			pg = tls.getEndpointService().getGroup();
		}
		 
		
		MembershipService ms = pg.getMembershipService();
        AuthenticationCredential ac = new AuthenticationCredential(pg,
        		"StringAuthentication", null);
        StringAuthenticator sa = null;

        try {
            sa = (StringAuthenticator) ms.apply(ac);
        } catch (ProtocolNotSupportedException pnse) {
        } catch (PeerGroupException pge) {
        }

        //sa.setAuth1_KeyStorePassword(keyStorePassword);
        sa.setAuth2Identity(pg.getPeerID());
        //sa.setAuth3_IdentityPassword(identityPassword);

        boolean isAuthenticated = sa.isReadyForJoin();

        if (isAuthenticated) {
            try {
                ms.join(sa);
            } catch (PeerGroupException pge) {
                pge.toString();
            }
        }
	        /*String dialogName = Dialog.getDialogNamer(OneToOneCommandDialog.class)
	                .getDialogName(peerGroup.getPeerName());*/
/*	        PipeAdvertisement pipeAdvertisment = PipeUtil.getAdv(peerGroup,
	                dialogName, PipeService.UnicastType, null);*/

	        /*if (group.isVisible()) {
	            PeerGroup cpg = AuthenticationUtil.getTLSPeerGroup(peerGroup);

	            if (!AuthenticationUtil.isAuthenticated(cpg)) {
	                System.out.println("authenticating");
	                }

	                // TODO Add authenticate
	                //AuthenticationUtil.authenticate(getView(), cpg);
	            }

	            if (!AuthenticationUtil.isAuthenticated(cpg)) {
	                System.out.println("not authenticated");
	            }
	        }*/

	        // tell the plugins that we have joined a group
	        //m_pluginContainer.notifyAboutGroupJoin(group);



	      /*  if (useAutoRdvMode) {
	            if (shouldBeAutoRdv(group)) {
	                setStatus(STRINGS.getString("status.group.rendezvous.auto")
	                        + ": " + group.getAutoRendezVousPeriod());
	                peerGroup.getRendezVousService().setAutoStart(useAutoRdvMode,
	                        group.getAutoRendezVousPeriod());
	            }
	        }*/
        
        chatPeerGroup = pg;
	}
	
	private PeerGroupAdvertisement create(PeerGroup parentGroup, String name, String description) throws Exception {
		PeerGroupAdvertisement pga;
		
		PeerGroupID id = PeerGroupID.create(new URI(GROUPIDSTR));
		
		ModuleImplAdvertisement implAdv =
			netPeerGroup.getAllPurposePeerGroupImplAdvertisement();

		// create the PeerGroupAdvertisment and publish it
		pga = (PeerGroupAdvertisement) AdvertisementFactory
				.newAdvertisement(PeerGroupAdvertisement.getAdvertisementType());
		pga.setPeerGroupID(id != null ? id : IDFactory.newPeerGroupID());
		pga.setName(name);
		pga.setDescription(description);
		pga.setModuleSpecID(implAdv.getModuleSpecID());

		DiscoveryService ds = parentGroup.getDiscoveryService();

		// Publish the group
		ds.publish(pga, 60*1000*60, 60*1000*60);
		ds.remotePublish(pga, 60*1000*60);

		return pga;
	}
	
	/*public void publishPeer(){
		
		long lifetime = 60 * 5 * 1000L;
        long expiration = 60 * 20 * 1000L;
        //long waittime = 60 * 3 * 1000L;
        //long waittime = 10 * 1000L;
        
		try
		{
			// Create an advertisement.
			PeerAdvertisement advertisement = (PeerAdvertisement)AdvertisementFactory.newAdvertisement(PeerAdvertisement.getAdvertisementType());
			
			// Create the advertisement data
			advertisement.setName(peerName);
			advertisement.setPeerID(IDFactory.newPeerID(this.chatPeerGroup.getPeerGroupID()));
			advertisement.setPeerGroupID(chatPeerGroup.getPeerGroupID());
			advertisement.setDescription("Test dscription");
			
			
			// Publish the new advertisement
			discovery.publish(advertisement, lifetime, expiration);
            discovery.remotePublish(advertisement, expiration);
            
            System.out.println("Advertisment published successfully");
		}
		catch(Exception e){
			e.printStackTrace();
		}				
	}*/
		
	public static PipeAdvertisement getPipeAdv(PeerGroup pg, String name,
			String type, byte[] idSeed) {
		PipeAdvertisement pa = searchLocal(pg, name);

		if (pa == null) {
			pa = createPipeAdv(pg, name, type, idSeed);

			publishPipeAdv(pg, pa);
		}

		return pa;
	}
	
	private static void publishPipeAdv(PeerGroup pg, PipeAdvertisement pa){
		DiscoveryService ds = pg.getDiscoveryService();

        try {
            ds.publish(pa);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
	}
	
	private static PipeAdvertisement createPipeAdv(PeerGroup pg, String name,
			String type, byte[] idSeed){
		PipeAdvertisement advertisement = (PipeAdvertisement)AdvertisementFactory.newAdvertisement(PipeAdvertisement.getAdvertisementType());

		PipeID pipeID = null;

		/*try {
			//pipeID = (PipeID) IDFactory.fromURI(chatPeerGroup.getPeerGroupID().toURI());
			pipeID = (PipeID) IDFactory.fromURI(new URI(PIPEIDSTR));
		} catch (URISyntaxException use) {
			use.printStackTrace();
		}*/

		pipeID = idSeed != null ? IDFactory.newPipeID(pg.getPeerGroupID(), idSeed) : 
								  IDFactory.newPipeID(pg.getPeerGroupID());
		
		//advertisement.setPipeID(IDFactory.newPipeID(PeerGroupID.defaultNetPeerGroupID));        
		//advertisement.setPipeID(IDFactory.newPipeID(chatPeerGroup.getPeerGroupID()));
		advertisement.setPipeID(pipeID);
		//advertisement.setPipeID(pipeID);
		advertisement.setType(type);        
		advertisement.setName(name);
		
		return advertisement;
	}
	
	// TODO Add verification that the server is up before sending the login
	
	public void sendLoginMessage(PipeAdvertisement bidiPipeAdv){
				
		createOutputPipe();
		
		MessageParams params = new MessageParams(peerName);
		params.addParameter(MessageParamsKeys.PIPE_ADV, bidiPipeAdv);
		
		AbstractMessage msg = MessageFactory.getInstance().getMessageClass(Constants.MessageTypes.LOGIN);
		
		// TODO Chage it to sendMessage call
		
		try {
			this.outputPipe.send(msg.createMessage(params));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("login message sent");		
	}
	
	private static PipeAdvertisement searchLocal(PeerGroup pg, String name) {
	    DiscoveryService discoveryService = pg.getDiscoveryService();
        Enumeration pas;
        try {
            pas = discoveryService.getLocalAdvertisements(DiscoveryService.ADV, PipeAdvertisement.NameTag, name);
        } catch (IOException e) {
            return null;
        }
        PipeAdvertisement advertisement;
        while (pas.hasMoreElements()) {
            advertisement = (PipeAdvertisement) pas.nextElement();

            if (advertisement.getName().equals(name)) {
                return advertisement;
            }
        }

		return null;
	}

	public void rendezvousEvent(RendezvousEvent arg0) {
		
		
	}

	// FIXME marked due to ant errors
	
	//@Override
	public void discoveryEvent(DiscoveryEvent ev) {
			
			DiscoveryResponseMsg res = ev.getResponse();
			String name = "unknown";
			
			System.out.println("In discoveryEvent");
			
			// Get the responding peer's advertisement
			/*PeerAdvertisement peerAdv = res.getPeerAdvertisement();
			// some peers may not respond with their peerAdv
			if (peerAdv != null) {
			name = peerAdv.getName();
			}
			System.out.println (" Got a Discovery Response [" + res.getResponseCount()+ " elements] from peer : " + name);
		
			// now print out each discovered peer group
			ModuleImplAdvertisement adv = null;
			Enumeration enum2 = res.getAdvertisements();
			
			if (enum2 != null ) {
			while (enum2.hasMoreElements()) {
			adv = (ModuleImplAdvertisement) enum2.nextElement();			
			System.out.println (" Provider = " + adv.getProvider());
			System.out.println (" Description = " + adv.getDescription());
			System.out.println (" Code = " + adv.getCode());
			}
			}*/
	        if (res.getDiscoveryType() == DiscoveryService.ADV) {
	        	Enumeration advertisements = res.getAdvertisements();
	        
	        	while (advertisements != null &&
		                advertisements.hasMoreElements()) {
		            Object o = advertisements.nextElement();

		            if (o instanceof PipeAdvertisement) {
		            	System.out.println("Recieved response from " + ((PipeAdvertisement) o).getName());		                		                
		                
		                }
		            }
		    }	        
		}
	
	public void sendMessage(Message msg){
		
		try {
            // send the message
            outputPipe.send(msg);	            
            
            System.out.println("message sent");
        } 
		catch (IOException e) {
            System.out.println("failed to send message");
            e.printStackTrace();
            System.exit(-1);
        }
		
	}
	
	public Message createMessage(String txt){
		String msgTxt = createMessageText(txt);
		
		AbstractMessage msg = MessageFactory.getInstance().getMessageClass(Constants.MessageTypes.REGULAR);
		
		// TODO Move the map to a dedicated class
		MessageParams params = new MessageParams(peerName);
		params.addParameter(MessageParamsKeys.MESSAGE, msgTxt);
		
		return msg.createMessage(params);
	}
	
	public String createMessageText(String txt){
		Date date = new Date(System.currentTimeMillis());
		
        // add a string message element with the current date
        SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
        String retVal = String.format("%s@%s: %s", peerName.substring(CHAT_PREFIX.length(), peerName.length()), 
        										   format.format(date), txt);
        
        return retVal;
	}
	
	public synchronized void createOutputPipe() {
        try {
        	
        	if (outputPipe == null){
        	
	        	outputCreated = false;
	        	
	        	PipeAdvertisement adv = getPipeAdv(chatPeerGroup, peerName, PipeService.PropagateType,
	        									   GROUP_CHAT_PIPE_ID);
	        	
	        	// TODO moved the publish to the create
	        	
	        	//this.chatPeerGroup.getDiscoveryService().publish(adv);
	        	
	            // issue a pipe resolution asynchronously. outputPipeEvent() is called
	            // once the pipe has resolved
	            pipeService.createOutputPipe(adv, new MyOutputPipeListener());            
	            //try {
	            
	            	// TODO Change it to the outputPipe
	            
	                while (!outputCreated) {
	                	//System.out.println("Start locking in createOutput");
	                    //lock.wait();
	                }
        	}        	
        } 
        catch (IOException e) {
            System.out.println("OutputPipe creation failure");
            e.printStackTrace();
            System.exit(-1);
        }
    }
	public void createInputPipe(){
		try {
	        System.out.println("Creating input pipe");
	        // Create the InputPipe and register this for message arrival
	        // notification call-back
	        inputPipe = pipeService.createInputPipe(getPipeAdv(chatPeerGroup, peerName, PipeService.PropagateType, GROUP_CHAT_PIPE_ID), new MyMsgListener());
	    } catch (IOException io) {
	        io.printStackTrace();
	        return;
	    }
	    if (inputPipe == null) {
	        System.out.println(" cannot open InputPipe");
	        System.exit(-1);
	    }
	    System.out.println("Waiting for msgs on input pipe");
	}
	
	public void addOutputListener(JxtaChatListener listener){
		outputListeners.add(listener);
	}
	
	// TODO Chek which listener we want to keep
	
	// TODO Change this method
	public void addPeerListener(JxtaChatListener listener){
		//peerSearcher.addChatEventListener(listener);
		peerListeners.add(listener);
	}
	
	private void notifyPeerEvent(JxtaChatEvent event){						
		
		for(JxtaChatListener listener : peerListeners){
			listener.jxtaChatEvent(event);
		}
	}
	
	private void notifyOutputEvent(JxtaChatEvent event){						
		
		for(JxtaChatListener listener : outputListeners){
			listener.jxtaChatEvent(event);
		}
	}
	
	/**
	 * Listener for the Pipe messages
	 * @author KobyKarin
	 *
	 */
	private class MyMsgListener implements PipeMsgListener{
		
		// TODO Verify that the current peer won;t deal with self log out
		
		/**
	     * PipeMsgListener interface for asynchronous message arrival notification
	     *
	     * @param event the message event
	     */
	    public void pipeMsgEvent(PipeMsgEvent event) {

	        Message msg;
	        try {
	            // Obtain the message from the event
	            msg = event.getMessage();
	            if (msg == null) {
	                System.out.println("Received an empty message");
	                return;
	            }
	            // dump the message content to screen
	            printMessageStats(msg, true);
	        } catch (Exception e) {
	            e.printStackTrace();
	            return;
	        }
	        
	        AbstractMessageHandler handler = MessageHandlerFactory.getInstance().getMessageHandler(msg, null);
	        AbstractMessage msg2 = MessageFactory.getInstance().getMessageClass(msg);
	        
	        // If it's a login for us, we'll exit
	        if ((handler == null) || (handler instanceof LoginMessageHandler && msg2.getPeerName() == peerName)){
	        	return;
	        }
	        
	        handler.addListener(new JxtaChatListener(){
				//@Override
				public void jxtaChatEvent(JxtaChatEvent event) {

					// If it's the current peer, we won't handle any messages
					/*if (peerName.equalsIgnoreCase(((AbstractMessage)event.getSource()).getPeerName())){
						return;
					}*/
					
					// Send a message back to the peer that just loged on
					if ((event.getSource() instanceof LoginMessage) && !peerName.equalsIgnoreCase(((LoginMessage)event.getSource()).getPeerName())){
						
						/*LoginMessage lm = (LoginMessage)event.getSource();
						
						MessageParams params = new MessageParams(peerName);
						params.addParameter(MessageParamsKeys.PIPE_ADV, pipeManager.getPipeAdv());					
						Message msg = lm.createMessage(params);
						
						System.out.println("sending pipe advertisment " + pipeManager.getPipeAdv().toString());
						
						// Send a message to the other side
						try {
							PipeHolder.getInstance().getPipe(lm.getPeerName()).sendMessage(msg);
						} catch (IOException e) {
							System.out.println("Error sending login message to other peer. error: " + e.getMessage());
						}*/
						if (event.getSource2() != null){
							((BidiPipeListener)event.getSource2()).addListener(new ChatBiDiPipeListener());
						}
					}
					
					notifyPeerEvent(event);
				}	        
	        });
	        
	        handler.handleMessage(msg, chatPeerGroup);
	    }
	    
	    public void printMessageStats(Message msg, boolean verbose) {
	        try {
	        	MessageElement e = msg.getMessageElement(null, MESSAGE_NAME_SPACE);	        		        	
	        	
	        	System.out.println("Got Message: " + e);
	        	
	        	// Searchin for peers
	        	//peerSearcher.search();
	        	
	        	} catch (Exception e) {
	            e.printStackTrace();

	        }
	    }
	}
	
	private class ChatInvitation implements PipeMsgListener{

		public void pipeMsgEvent(PipeMsgEvent arg0) {
			
			AbstractMessage msg = MessageFactory.getInstance().getMessageClass(arg0.getMessage());
			
			if (msg == null){
				System.out.println("Got null message in ChatInvitation");
				return;
			}
			
			//VoiceChatMessage msg = (VoiceChatMessage)arg0.getSource();
			Object response = msg.getAttributeValue(Constants.CHAT_RESPONSE_ATTRIBUTE);
			if (response != null){
				ChatInvitaionResponseVals chatResponse = (ChatInvitaionResponseVals)response;
				
				if (screen.presentInvitationResponse(chatResponse)){
					chatInProgress = true;
					chatingPeerName = screen.getSelectedPeerName(); 
					screen.startVoiceChat(true);
				}
			}
		}
		
	}
	
	private class ChatBiDiPipeListener implements JxtaChatListener{

		public void jxtaChatEvent(JxtaChatEvent event) {
			
			// Add connected peer to the pipe holder
			if (event.getSource() instanceof LoginMessage){
				Message msg = ((LoginMessage)event.getSource()).getMessage();
				
				AbstractMessageHandler handler = MessageHandlerFactory.getInstance().getMessageHandler(msg, null);
				handler.handleMessage(msg, chatPeerGroup);
			}
			else if (event.getSource() instanceof VoiceChatMessage){
				if (isChatInProgress()){
					
					VoiceChatMessage source = (VoiceChatMessage)event.getSource();
					
					if (chatingPeerName.equalsIgnoreCase(source.getPeerName()) &&
						Constants.ChatRequestVals.CALL_REQUEST.toString().equalsIgnoreCase(source.getAttributeValue(Constants.CHAT_REQUEST_ATTRIBUTE).toString())){
						screen.updateChatScreen();
					}
					else{
					
						MessageParams pm = new MessageParams(peerName);
						pm.addParameter(MessageParamsKeys.RESPONSE, ChatInvitaionResponseVals.BUSY);						
						AbstractMessage am = MessageFactory.getInstance().getMessageClass(MessageTypes.VOIP_CHAT);						
						Message m = am.createMessage(pm);
						
						(new RemoteMessageSender()).sendMessage(JxtaManager.this, m, ((AbstractMessage)event.getSource()).getPeerName());
					}
					
					// We responded to the invitation so we'll exit
					return;
				}				
				else{
					
					VoiceChatMessage request = ((VoiceChatMessage)event.getSource());
					
					boolean validated = screen.respondChatInvitation(((VoiceChatMessage)event.getSource()).getPeerName());										
					
					MessageParams pm = new MessageParams(peerName);
					
					if (validated){																	
						pm.addParameter(MessageParamsKeys.RESPONSE, ChatInvitaionResponseVals.ACCEPT);												
					}
					else{
						pm.addParameter(MessageParamsKeys.RESPONSE, ChatInvitaionResponseVals.REFUSE);
					}
					
					AbstractMessage am = MessageFactory.getInstance().getMessageClass(MessageTypes.VOIP_CHAT);						
					Message m = am.createMessage(pm);
					
					(new RemoteMessageSender()).sendMessage(JxtaManager.this, m, ((AbstractMessage)event.getSource()).getPeerName());
					
					if (validated){
						chatInProgress = true;
						chatingPeerName = ((AbstractMessage)event.getSource()).getPeerName();
						screen.startVoiceChat(false, ((AbstractMessage)event.getSource()).getPeerName());
					}
					
					return;
				}								
			}
			
			notifyPeerEvent(event);
		}
		
	}
	
	private class MyOutputPipeListener implements OutputPipeListener{

		// FIXME marked due to ant errors
		
		//@Override
		public void outputPipeEvent(OutputPipeEvent arg0) {
	        // get the output pipe object
	        outputPipe = arg0.getOutputPipe();

	        outputCreated = true;
			
		}
		
	}

	public static String getPeerName() {
		return peerName;
	}

	public boolean isWaitingForChatInvetation() {
		return waitingForInvetation;
	}

	public boolean isChatInProgress() {
		return chatInProgress;
	}

	public void setChatInProgress(boolean chatInProgress) {
		this.chatInProgress = chatInProgress;
	}
}
